/**
 * 
 */
package wdmsim;

import java.util.Map;
import java.util.Vector;
import multidomain.Domain;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import controlPlane.OBGPSpeaker;
import controlPlane.PCE;
import util.Auxiliar;
import util.Dijkstra;
import util.WeightedGraph;

/**
 * @author alissonslp
 *
 */
public class PhysicalTopology {

	/** number of domains */
	private int domains;
	/** number of nodes */
    private int nodes;
    /** number of interdomain virtual links*/
    private int domainLinks;
    /** total number of links */
    private int links;    
    /** number of available wavelengths */
    private int wavelengths;
    /** vector of domains */
    private Domain[] domainVector;
    /** array of OxCs*/
    private OXC[] nodeVector;
    /** array of intradomain links */
    private WDMLink[] linkVector;
    /** simple node abstraction adjacency matrix (domains neighborhood)*/
    private boolean[][] adjacencyDomainMatrix;
    /** nodes adjacency matrix*/
    private WDMLink[][] adjacencyMatrix;
	private Vector<WDMLink> interdomainLinkVector;
    
	/** */
    private static int linksID = 0;
    /** Singleton object to implement a static class 
     * This is useful when exactly one object is needed to coordinate actions across the system
     */
    protected static PhysicalTopology singletonObject;
    
    /**
     * 
     * @param xml
     */
	public PhysicalTopology() {
		Element xml = (Element) Simulator.DOC.getElementsByTagName("physical-topology").item(0);
    	
		try {
			if (Simulator.VERBOSE) {
				System.out.println(xml.getAttribute("name"));
			}
		    
			wavelengths = Integer.parseInt(xml.getAttribute("wavelengths"));           
		   
			if (Simulator.IS_MULTIDOMAIN) {
				processDomains(xml);
				processMultiDomainLinkage(xml);        	   
			}
			processNodes(xml);
			processLinks(xml);
			  
			if(!Simulator.RWA_MODE.equalsIgnoreCase("pce-based")){   
				// Initialize Control Plane and Virtual Topologies of each domain
				//Domain.initiateDomainsStructures();
			}
			        
		} catch (Throwable t) {
			t.printStackTrace();
		}
    }
	
	/**
	 * 
	 * @return
	 */
	public static synchronized PhysicalTopology getInstance() {
    	if (singletonObject == null) 
    		singletonObject = new PhysicalTopology();
    	return(singletonObject);
    }
	
	
	//////////////////////////////////////////////////////////////////////////
	// methods for physical topology construction
	//
	
	/**
	 * 
	 * @param xml
	 */
	private void processNodes(Element xml) {
		//esse metodo foi modificado durante implementacao do obgp
		int id, domainID, wvlConversionRange;
		boolean borderNode;

		NodeList nodelist = xml.getElementsByTagName("node");
	    nodes = nodelist.getLength();
	    if (Simulator.VERBOSE) {
	        System.out.println(Integer.toString(nodes) + " nodes");
	    }
	    
	    nodeVector = new OXC[nodes];
	    
	    for (int i = 0; i < nodes; i++) {
	        id = Integer.parseInt(((Element) nodelist.item(i)).getAttribute("id"));
	        borderNode = Boolean.parseBoolean(((Element) nodelist.item(i)).getAttribute("borderNode"));
	        
	        if (Simulator.IS_MULTIDOMAIN) {
	        	domainID = Integer.parseInt(((Element) nodelist.item(i)).getAttribute("domain-id"));
	        	
	        } else { 
	        	domainID = -1;
	        }
	        wvlConversionRange = Integer.parseInt(((Element) nodelist.item(i)).getAttribute("wlconversion-range"));
	        
	        if(borderNode==true) {
	        	nodeVector[id] = new OBGPSpeaker(id, domainID, wvlConversionRange);
	        	
	        } else {
	        	nodeVector[id] = new OXC(id, domainID, wvlConversionRange);
	        }
	       	
	       	if (Simulator.IS_MULTIDOMAIN) 
	       		domainVector[domainID].addNode(nodeVector[id]);
	    }
	}
	
	/**
	 * 
	 * @param xml
	 */
	private void processLinks(Element xml) {
		int src, dst, bw, weight;
    	double delay;
    	String type;
    	
    	NodeList linklist = xml.getElementsByTagName("link");
        links = linklist.getLength();
        
        if (Simulator.VERBOSE) {
            System.out.println(Integer.toString(links) + " links");
        }
        linkVector = new WDMLink[links * 2]; //If every link is a duplex, at most 2 * links will be created
        adjacencyMatrix = new WDMLink[nodes][nodes];
        interdomainLinkVector = new Vector<WDMLink>();
        
        for (int i = 0; i < links; i++) {
        	src = Integer.parseInt(((Element) linklist.item(i)).getAttribute("source"));
			dst = Integer.parseInt(((Element) linklist.item(i)).getAttribute("destination"));
			delay = Double.parseDouble(((Element) linklist.item(i)).getAttribute("delay"));
			bw = Integer.parseInt(((Element) linklist.item(i)).getAttribute("bandwidth"));
			weight = Integer.parseInt(((Element) linklist.item(i)).getAttribute("bandwidth"));
			type = ((Element) linklist.item(i)).getAttribute("type");
			              
			newLink(src, dst, delay, bw, weight);
			
			if (type.equals("duplex")) {
				newLink(dst, src, delay, bw, weight);
			}
		}
	}
	
	/**
	 * @param src
	 * @param dst
	 * @param delay
	 * @param bw
	 * @param weight
	 */
	private void newLink(int src, int dst, double delay, int bw, int weight) {

		// Se src e dst forem o mesmo node: ERRO
        if (src == dst) {
        	System.out.println("A node can't be connected to itself (Node ID " + src + ")");
        	System.exit(-1);
        }
        // se ha um link interdominio
        if(!belongsSameDomain(src, dst)) {
        	interdomainLinkVector.add(new WDMLink(linksID, src, dst, delay, wavelengths, bw, weight));
        	linkVector[linksID] = adjacencyMatrix[src][dst] = interdomainLinkVector.lastElement();
        	
        }
        else {
        	linkVector[linksID] = adjacencyMatrix[src][dst] = new WDMLink(linksID, src, dst, delay, wavelengths, bw, weight);
        	
        }
        linksID++;
	}

	/**
	 * 
	 * @param xml
	 */
	private void processDomains(Element xml) {
		int i, id, load, maxRate;
    	//String rwaModuleName, rwaMode, rwaParameters;
    	    	
    	//Domain.clearStaticVariables();
    	NodeList domainlist = xml.getElementsByTagName("domain");
        domains = domainlist.getLength();
        
        if (Simulator.VERBOSE) 
        	System.out.println(Integer.toString(domains) + " domains");
        
        domainVector = new Domain[domains];
        
        for(i = 0; i < domains; i++) {
        	id = Integer.parseInt(((Element) domainlist.item(i)).getAttribute("id"));
        	
        	if (domainVector[id] != null) {
        		System.err.println("Domain id " + id + " is repeated. Fix it first.");
        		System.exit(-1);
        	}
        	
        	//rwaModuleName = "wdmsim.rwa." + String.valueOf(((Element) domainlist.item(i)).getAttribute("rwa-module"));
        	//rwaParameters = String.valueOf(((Element) domainlist.item(i)).getAttribute("rwa-arguments"));
        	//rwaMode = String.valueOf(((Element) domainlist.item(i)).getAttribute("rwa-mode"));
        	load = Integer.valueOf(((Element) domainlist.item(i)).getAttribute("load"));
        	maxRate = Integer.valueOf(((Element) domainlist.item(i)).getAttribute("max-rate"));        	
        	
        	domainVector[i] = new Domain(id, /*rwaModuleName, rwaParameters, rwaMode,*/ load, maxRate);
        	if(Simulator.VERBOSE)
        		System.out.println("ID = " + id + "; LOAD = " + load + " erlangs; MAX RATE = " + maxRate + "MB/s");
        		//System.out.println("ID = " + id + "; RWA MODULE NAME = " + rwaModuleName + "; RWA MODE = " + rwaMode + "; LOAD = " + load + "; MAX RATE = " + maxRate);
        }
	}
	
	/**
	 * 
	 * @param xml
	 */
	private void processMultiDomainLinkage(Element xml) {
		
		int src, dst;
    	String type;
    	NodeList domainlinklist = xml.getElementsByTagName("multidomain-linkage");
        domainLinks = domainlinklist.getLength();
        if (Simulator.VERBOSE) {
            System.out.println(Integer.toString(domainLinks) + " interdomain-linkage");
        }
     
        adjacencyDomainMatrix = new boolean[domains][domains];
                
        for (int i = 0; i < domainLinks; i++) {
            src = Integer.parseInt(((Element) domainlinklist.item(i)).getAttribute("source"));
            dst = Integer.parseInt(((Element) domainlinklist.item(i)).getAttribute("destination"));
            type = ((Element) domainlinklist.item(i)).getAttribute("type");
            
            newInterDomainLinkage(src, dst);
            if (type.equals("duplex"))
            	newInterDomainLinkage(dst, src);
        }
	}
	
	//public void processPCEs() throws Exception {
	//	Domain.processPCEs();
	//}
	
	/**
	 * @param src
	 * @param dst
	 */
	private void newInterDomainLinkage(int src, int dst) {
		
	    	if (src == dst) {
	        	System.out.println("Same domains on interdomain-linkage. ID = " + src);
	        	System.exit(-1);
	        }
	        if (src >= domains) {
	        	System.out.println("Source domain with id " +  src + " doesn't exist.");
	        	System.exit(-1);
	        }
	        if (dst >= domains) {
	        	System.out.println("Destination domain with id " + dst + " doesn't exist.");
	        	System.exit(-1);
	        }
	        
	        adjacencyDomainMatrix[src][dst] = true;      
	     
	        if(Simulator.VERBOSE)
	        	System.out.println("Linkage = " + src + " " + dst);
	}
	
	//////////////////////////////////////////////////////////////////////////////////////
	// methods for physical topology manipulation
	//	
	
	/**
	 * 
	 */
	public Domain getDomainFromNode(int nodeID) throws Exception {
    	int i = 0;
    	
    	if (Simulator.IS_MULTIDOMAIN) {
    		while ((i < nodes) && (nodeVector[i].getId() != nodeID))
    			i++;
    	
    		if (i >= nodes) {
    			System.out.println("Error on getDomainFromNode. Node with ID " + nodeID + " doesn't exist");
    			throw new Exception();
    		}
    	
    		return(Domain.getDomainByID(nodeVector[i].getDomainID()));
    	} else
    		return(null);
    }

	/**
	 * 
	 * @param i
	 * @return
	 */
	public Domain getDomain(int i) {
		return domainVector[i];
	}
	
	/**
	 * 
	 * @return
	 */
	public boolean[][] getAdjacencyDomainMatrix() {
        return adjacencyDomainMatrix;
    }
	
	/**
	 * 
	 * @return
	 */
	public int getNumNodes() {
        return nodes;
    }
	
	/**
	 * 
	 * @param id
	 * @return
	 */
	public OXC getNode(int id) {
		return nodeVector[id];
	}
	 
	/**
	 * 
	 * @return
	 */
	public WDMLink[][] getAdjacencyMatrix() {
		return adjacencyMatrix;
	}

	/**
	 * 
	 * @return
	 */
	public Domain[] getDomainVector() {
		return domainVector;
	}

	/**
	 * 
	 * @return
	 */
	public int getWavelengths() {
		return wavelengths;
	}
	
	/**
	 * 
	 * @param src
	 * @param dst
	 * @return
	 */
	public WDMLink getLink(int src, int dst) {
        return adjacencyMatrix[src][dst];
    }
	
	/**
	 * 
	 * @return
	 */
	public OXC[] getNodeVector() {
		return nodeVector;
	}
	
	/**
	 * retorna o borderNode de outDomain que tem interdomainLink com domainId
	 * 
	 * @param domainId id do dominio fonte
	 * @param outDomain id do dominio vizinho
	 */
	public OXC getInBorderNode(int domainId, int outDomain) throws Exception {
		OXC borderNode = null;
		for(int i=0; i<interdomainLinkVector.size(); i++) {
			// se eh um enlace entre um node de domainId e um node de outDomain
			if(getDomainFromNode(interdomainLinkVector.get(i).getDstId()).getID()==outDomain &&
					getDomainFromNode(interdomainLinkVector.get(i).getSrcId()).getID()==domainId) {
				borderNode = getNode(interdomainLinkVector.get(i).getDstId());
				break;
			}
		}
		return borderNode;
	}
	
	/**
	 * retorna o i-esimo borderNode de domainId
	 * 
	 * @param domainId
	 * @param i
	 */
	public OXC getBorderNode(int domainId, int i) {
		int cont = -1;
		int j = -1;
		Domain domain = getDomain(domainId);
		Vector<WDMLink> linkVector = getInterdomainLinkVector();
		do {
			j++;
			if(domain.nodeBelongsToDomain(linkVector.get(j).getSource()))
				cont++;
			
		} while(j<linkVector.size() && cont!=i);
		return getNode(linkVector.get(j).getSource());
	}
	/**
	 * @param i
	 * @param j
	 * @return
	 * @throws Exception 
	 */
	public WDMLink getInterdomainLink(int dsrc, int ddst) throws Exception {
		for(int i=0; i<interdomainLinkVector.size(); i++) {
			
			if(getDomainFromNode(interdomainLinkVector.get(i).getSrcId()).getID()==dsrc && 
					getDomainFromNode(interdomainLinkVector.get(i).getDstId()).getID()==ddst) {
				return interdomainLinkVector.get(i);
			}
		}
		return null;
	}

	/**
	 * 
	 * @return
	 */
	public int getNumDomains() {
		return domains;
	}
	
	/**
	 *  retorna o borderNode de domainId que tem interdomainLink com outDomain
	 * @param domainId
	 * @param outDomain
	 * @return
	 * @throws Exception
	 */
	public OXC getOutBorderNode(int domainId, int outDomain) throws Exception {
		OXC borderNode = null;
		for(int i=0; i<interdomainLinkVector.size(); i++) {
			// se eh um enlace entre um node de domainId e um node de outDomain
			if(getDomainFromNode(interdomainLinkVector.get(i).getDstId()).getID()==outDomain &&
					getDomainFromNode(interdomainLinkVector.get(i).getSrcId()).getID()==domainId) {
				borderNode = getNode(interdomainLinkVector.get(i).getSrcId());
				break;
			}
		}
		return borderNode;
	}
	
	/**
	 * 
	 * @param domainId
	 * @return
	 */
	public int getNumBorderNodes(int domainId) {
		int cont = 0;
		Vector<OXC> nodes = new Vector<OXC>();
		Domain domain = getDomain(domainId);
		Vector<WDMLink> linkVector = getInterdomainLinkVector();
		for(int i=0; i<linkVector.size(); i++) {
			OXC node = getNode(linkVector.get(i).getSource());
			if(domain.nodeBelongsToDomain(node.getId()) && !nodes.contains(node)) {
				cont++;
				nodes.add(node);
			}
				
		}
		return cont;
	}
	
	public boolean isBorderNode(int nodeId) {
		Vector<WDMLink> linkVector = getInterdomainLinkVector();
		for(int i=0; i<linkVector.size(); i++) {
			WDMLink link = linkVector.get(i);
			if(link.getSrcId()==nodeId) {
				return true;
			}
		}
		return false;
	}
	/**
	 * 
	 * @param adjacencyMatrix
	 */
	 public void setAdjacencyMatrix(WDMLink[][] adjacencyMatrix) {
		this.adjacencyMatrix = adjacencyMatrix;
	}
	
	 /**
	  * 
	  * @param domainVector
	  */
	public void setDomainVector(Domain[] domainVector) {
		this.domainVector = domainVector;
	}
	
	/**
	 * 
	 * @param nodeVector
	 */
	public void setNodeVector(OXC[] nodeVector) {
		this.nodeVector = nodeVector;
	}

	/**
	 * 
	 * @param domains
	 */
	public void setDomains(int domains) {
		this.domains = domains;
	}
	
	/**
	 * 
	 * @param node1
	 * @param node2
	 * @return
	 */
	public boolean belongsSameDomain(int node1, int node2) {
		int domain1=0;
		int domain2=0;
		for(int i=0; i<nodeVector.length; i++) {
			if(nodeVector[i].getId() == node1) domain1 = nodeVector[i].getDomainID(); 
			if(nodeVector[i].getId() == node2) domain2 = nodeVector[i].getDomainID();
		}
		
		if(domain1==domain2) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * 
	 * @param node1
	 * @param node2
	 * @return
	 */
	public boolean hasLink(int node1, int node2) {
        if (adjacencyMatrix[node1][node2] != null) {
            return true;
        } else {
            return false;
        }
    }
	
	/**
	 * 
	 * @param d1
	 * @param d2
	 * @return
	 */
	public boolean hasInterDomainLink(int d1, int d2) {
		if (Simulator.IS_MULTIDOMAIN)
    		return(adjacencyDomainMatrix[d1][d2]);
    	else
    		return(false);
    }
    
	/**
	 * 
	 * @param d1
	 * @param d2
	 * @return
	 */
    public boolean areNeighbourDomains(int d1, int d2) {
    	if(Simulator.IS_MULTIDOMAIN)
    		return(hasInterDomainLink(d1, d2));
    	else
    		return(false);
    }
    
    ///////////////////////////////////////////////////
    // methods for path computation
    //

	/**
     * Returns a weighted graph with vertices, edges and weights representing
     * the physical network nodes, links and weights implemented by this class
     * object.
     * 
     * @return	a WeightedGraph class object 
	 * @throws Exception 
     */
    public WeightedGraph getWeightedGraph() throws Exception {
        WeightedGraph g = new WeightedGraph(nodes);
        for (int i = 0; i < nodes; i++) {
            for (int j = 0; j < nodes; j++) {
                if (hasLink(i, j)) {
                    g.addEdge(i, j, getLink(i, j));
                }
            }
        }
        return g;
    }
    
    /**
     * 
     * @return
     * @throws Exception
     */
    public WeightedGraph getDomainWeightedGraph() throws Exception {
    	WeightedGraph g = new WeightedGraph(domains);
    	
    	for(int i = 0; i < domains; i++)
    		for(int j = 0; j < domains; j++) {
    			if (hasInterDomainLink(i, j))
    				g.addEdge(i, j, getInterdomainLink(i, j));
    				//g.addEdge(i, j, 1);
    		}
    	return(g);
    }
    
    /**
     * 
     * @param domainId
     * @return
     * @throws Exception
     */
    public WeightedGraph getIntradomainWeightedGraph(int domainId) throws Exception {
    	WeightedGraph g = new WeightedGraph(nodes);
        for (int i = 0; i < nodes; i++) {
        	if(getDomainFromNode(i).getID()==domainId) {
	            for (int j = 0; j < nodes; j++) {
	            	if(getDomainFromNode(j).getID()==domainId) {
		                if(hasLink(i, j)) {
		                    g.addEdge(i, j, getLink(i, j));
		                }
	            	}
	            }
		        
        	} else {
        		g.removeNode(i);
        	}
        }
        return g;
    }
	
	/**
	 * 
	 * @param domainId
	 * @param src
	 * @param dst
	 * @param resources
	 * @param wavelength
	 * @return
	 * @throws Exception
	 */
	public int[] getIntradomainPath(int domainId, int src, int dst, int flowSrc, int resources, int wavelength) throws Exception {
		if(src==dst) {
			int[] r = new int[1]; r[0] = src; return r;
		}

		WeightedGraph graph = getIntradomainWeightedGraph(domainId);
		boolean bool = false;
		int path[];
		
		do {
			path = Dijkstra.getShortestPath(graph, src, dst);
			bool = false;
			int i;			
			
			if(path!=null && path.length>0) {
				//bool = true;
				for(i=0; i<path.length-1; i++) {
					WDMLink link = getLink(path[i], path[i+1]);
					//if(!(link.getWavelengthAvailableBandwidth(wavelength)>=resources && (link.getOwn(wavelength)==-1 || link.getOwn(wavelength)==src))) {
					if(link.getWavelengthAvailableBandwidth(wavelength)>=resources && (link.getOwn(wavelength)==-1 || link.getOwn(wavelength)==flowSrc)) {
						bool = true;
					} else {
						bool = false;
						break;
					}
				}
				
			} else {
				return null;
			}
			if(bool && path.length>0 ) {
				break;
				
			} else {
				graph.removeEdge(path[i], path[i+1]);
			}
		}while(path.length>0);
		
		if(bool)
			return path;
		else
			return null;
	}
	
	/**
	 * @param id
	 * @param id2
	 * @return
	 * @throws Exception 
	 */
	public int[] getIntradomainPath(int src, int dst, int resources, int[] wv) throws Exception {
		if(src==dst) {
			int[] r = new int[1]; r[0] = src; return r;
		}
		WeightedGraph graph = getWeightedGraph();
		boolean bool = false;
		if(Dijkstra.getShortestCost(graph, src, dst)>=resources) {
			int[] path = Dijkstra.getShortestPath(graph, src, dst);
			int[] wv2 = getPathAvailableWavelengths(path, resources);
			if(wv2.length>0) {
					for(int i=0; i<wv.length && i<wv2.length; i++) {
						if(wv[i]>wv2[i]) {
							wv[i] = wv2[i];
						}
					}
					for(int i=0; i<wv.length; i++) {
						if(wv[i]>=resources){
							bool = true;
							break;
						}
					}
					if(bool)
						return path;
					else
						return null;
			}
			else
				return null;
		}
		else {
			return null;
		}
	}
	
	/**
	 * 
	 * @param src
	 * @param dst
	 * @param resources
	 * @return
	 * @throws Exception
	 */
	public int[] getIntradomainPath(int src, int dst, int resources) throws Exception {
		if(src==dst) {
			int[] r = new int[1]; r[0] = src; return r;
		}
		WeightedGraph graph = getWeightedGraph();
		boolean bool = false;
		// se src==dst retorna um array de uma posi�ao contendo o id de src
		if(src==dst) {
			int[] r = new int[1];
			r[0] = src;
			return r;
		}
		else {
			if(Dijkstra.getShortestCost(graph, src, dst)>=resources) {
				int[] path = Dijkstra.getShortestPath(graph, src, dst);
				int[] wavelengths = getPathAvailableWavelengths(path, resources);
				for(int i=0; i<wavelengths.length; i++) {
					if(wavelengths[i]>=resources) {
						bool = true;
						break;
					}
				}
				if(bool)
					return path;
				else
					return null;
			}
			else {
				return null;
			}
		}
	}
	
	/**
	 * Retorna o menor caminho entre srcDomain e dstDomain,
	 * nao considera restri�ao de comprimento de onda
	 * 
	 * @param srcDomain
	 * @param dstDomain
	 * @return vetor com ids dos dominios do caminho
	 * @throws Exception
	 */
	public int[] getDomainPath(int srcDomain, int dstDomain) throws Exception {
		WeightedGraph graph = getDomainWeightedGraph();
		return Dijkstra.getShortestPath(graph, srcDomain, dstDomain);		
	}
	
	/**
	 * 
	 * @param graph
	 * @param srcDomain
	 * @param dstDomain
	 * @return
	 * @throws Exception
	 */
	public int[] getDomainPath(WeightedGraph graph, int srcDomain, int dstDomain) throws Exception {
		return Dijkstra.getShortestPath(graph, srcDomain, dstDomain);		
	}
	
	public int[] getDomainPathF(WeightedGraph graph, int srcDomain, int dstDomain) throws Exception {
		return Dijkstra.getShortestPathF(graph, srcDomain, dstDomain);		
	}

	/**
	 * Calcula o custo dos enlaces multidominio entre srcDomain e dstDomain.
	 * Nao considera restri�ao de comprimento de onda.
	 * 
	 * @param id
	 * @param dst
	 * @throws Exception 
	 */
	public int getMultidomainCost(int srcDomain, int dstDomain) throws Exception {
		WeightedGraph graph = getDomainWeightedGraph();
		int[] path = Dijkstra.getShortestPath(graph, srcDomain, dstDomain);
		int cost = 0;
		
		for(int i=0; i<path.length-1; i++) {
        	cost += graph.getWeight(path[i], path[i+1]);
        }
		return cost;
	}
	
	/**
	 * 
	 * @param srcDomain
	 * @param dstDomain
	 * @return
	 * @throws Exception
	 */
	public int[] getMultidomainWavelengthCost(int srcDomain, int dstDomain) throws Exception {
		WeightedGraph graph = getDomainWeightedGraph();
		int[] costs = new int[wavelengths];
		int[] path = Dijkstra.getShortestPath(graph, srcDomain, dstDomain);
		for(int i=0; i<wavelengths; i++) {
			costs[i] = 0;
		}
		for(int i=0; i<path.length-1; i++) {
			for(int j=0; j<wavelengths; j++) {
				WDMLink link = (WDMLink)graph.getEdge(path[i], path[i+1]);
				costs[j] += link.getWavelengthAvailableBandwidth(j);
			}
        }
		return costs;
	}
	
	/**
	 * 
	 * @param path
	 * @param srcDomain
	 * @param dstDomain
	 * @return
	 * @throws Exception
	 */
	public int[] getMultidomainWavelengthCost(int[] path, int srcDomain, int dstDomain) throws Exception {
		WeightedGraph graph = getDomainWeightedGraph();
		int[] costs = new int[wavelengths];
		//int[] path = Dijkstra.getShortestPath(graph, srcDomain, dstDomain);
		for(int i=0; i<wavelengths; i++) {
			costs[i] = 0;
		}
		for(int i=0; i<path.length-1; i++) {
			for(int j=0; j<wavelengths; j++) {
				WDMLink link = (WDMLink)graph.getEdge(path[i], path[i+1]);
				costs[j] += link.getWavelengthAvailableBandwidth(j);
			}
        }
		return costs;
	}
	
	/**
	 * 
	 * @param graph
	 * @param path
	 * @param srcDomain
	 * @param dstDomain
	 * @return
	 * @throws Exception
	 */
	public int[] getMultidomainWavelengthCost(WeightedGraph graph, int[] path, int srcDomain, int dstDomain) throws Exception {
		//WeightedGraph graph = getDomainWeightedGraph();
		int[] costs = new int[wavelengths];
		//int[] path = Dijkstra.getShortestPath(graph, srcDomain, dstDomain);
		for(int i=0; i<wavelengths; i++) {
			costs[i] = 0;
		}
		for(int i=0; i<path.length-1; i++) {
			for(int j=0; j<wavelengths; j++) {
				WDMLink link = (WDMLink)graph.getEdge(path[i], path[i+1]);
				costs[j] += link.getWavelengthAvailableBandwidth(j);
			}
        }
		return costs;
	}
	
	public int[] getPathWavelengthAvailability(WeightedGraph graph, int[] path, int src) throws Exception {
		int[] availability = new int[wavelengths];
		if(path!=null && path.length>0) {
			for(int i=0; i<wavelengths; i++) {
				availability[i] = Integer.MAX_VALUE;
			}
			Vector<Integer> wvNotAvail = new Vector<Integer>();
			for(int i=0; i<path.length-1; i++) {
				for(int j=0; j<wavelengths; j++) {
					WDMLink link = (WDMLink)graph.getEdge(path[i], path[i+1]);
					if((link.getOwn(j)==src || link.getOwn(j)==-1) 
							&& link.getWavelengthAvailableBandwidth(j)<availability[j] && !wvNotAvail.contains(j)) {
						availability[j] = link.getWavelengthAvailableBandwidth(j);
						
					} else {
						availability[j] = 0;
						wvNotAvail.add(j);
					}
				}
	        }
		}
		return availability;
	}
	/**
	 * 
	 * @param graph
	 * @param path
	 * @return
	 * @throws Exception
	 */
	public int[] getPathWavelengthCost(WeightedGraph graph, int[] path) throws Exception {
		int[] costs = new int[wavelengths];
		for(int i=0; i<wavelengths; i++) {
			costs[i] = 0;
		}
		for(int i=0; i<path.length-1; i++) {
			for(int j=0; j<wavelengths; j++) {
				WDMLink link = (WDMLink)graph.getEdge(path[i], path[i+1]);
				costs[j] += link.getWavelengthAvailableBandwidth(j);
			}
        }
		return costs;
	}
	
	// intradomain
	public int[] getPathWavelengthCost(int[] path, int node) throws Exception {
		int[] costs = new int[wavelengths];
		for(int i=0; i<wavelengths; i++) {
			costs[i] = 0;
		}
		for(int i=0; i<path.length-1; i++) {
			for(int j=0; j<wavelengths; j++) {
				WDMLink link = (WDMLink)getLink(path[i], path[i+1]);
				//if(link.getOwn(j)==node || link.getOwn(j)==-1) {
					costs[j] += link.getWavelengthAvailableBandwidth(j);
				//}
			}
        }
		return costs;
	}

	/**
	 * @return
	 */
	public Vector<WDMLink> getInterdomainLinkVector() {
		return interdomainLinkVector;
	}

	/**
	 * 
	 * @param flow
	 * @param pce
	 * @return
	 * @throws Exception
	 */
	public boolean destroyLightpath(Flow flow, PCE pce) throws Exception {
		Path path = flow.getPath();
		boolean destroyed = false;
		
		Map<Flow, Path> activeFlows = pce.getActiveFlows();
		if(activeFlows.containsKey(flow)) {
			destroyed = true;
			
			for(int i=0; i<path.getLightpaths().size(); i++) {
				if(!path.getLightpaths().get(i).releaseLightpath(flow, pce)) {
					destroyed = false;
					break;
				}
			}
			for(int i=0; i<path.getLightpaths().size(); i++) {
				if(path.getLightpaths().get(i).isEmpty()) {
					path.removeLightpath(path.getLightpaths().get(i));
				}
			}
			
		}
		return destroyed;
	}
		
	/**
	 * Retorna a quantidade de bandwidth disponivel em cada 
	 * wavelength do caminho domainPath.
	 * 
	 * @param path
	 * @return
	 * @throws Exception
	 */
	public int[] getPathAvailableWavelengths(int[] path, int node) throws Exception {
		int[] r = new int[wavelengths];
		for(int i=0; i<wavelengths; i++){
			r[i] = Integer.MAX_VALUE;
		}
		for(int i=0; i<path.length-1; i++) {
			WDMLink link = getLink(path[i], path[i+1]);
			for(int j=0; j<link.getWavelengths(); j++) {
				//if(link.getOwn(j)==node || link.getOwn(j)==-1) {
					r[j] = Math.min(r[j], link.getWavelengthAvailableBandwidth(j));
				
				//} else {
				//	r[j] = 0;
				//}
			}
		}
		return r;
	}
	
	/**
	 * @param path
	 * @return
	 */
	public int[] getPathFreeWavelengths(int[] path) {
		int[] r = null;
		int[] init = new int[wavelengths];
		for(int i=0; i<init.length; i++) {
			init[i] = i;
		}
		WDMLink link1 = getLink(path[0], path[1]);
		r = Auxiliar.intIntersection(init, link1.getFreeWavelengths());
		for(int i=1; i<path.length-1; i++) {
			WDMLink link = getLink(path[i], path[i+1]);
			r = Auxiliar.intIntersection(r, link.getFreeWavelengths());
		}
		//System.out.print("");
		return r;
	}
	
	public int[] getPathAvailableBandWidth(int[] path) {
		int[] r = new int[path.length -1];
		int[] aux;
		/*for (int i = 0; i< path.length-1; i++) {
			WDMLink link1 = getLink(path[i], path[i+1]);
			aux = link1.getWavelenghtsWithMaxAvailableBandWidth();
			if (aux == null) {
				return new int[0];
			}
			r[i] = (int)(Math.random() * aux.length);
		}*/
		int[] init = new int[wavelengths];
		for(int i=0; i<init.length; i++) {
			init[i] = i;
		}
		WDMLink link1 = getLink(path[0], path[1]);
		r = Auxiliar.intIntersection(init, link1.getWavelenghtsWithMaxAvailableBandWidth());
		for(int i=1; i<path.length-1; i++) {
			WDMLink link = getLink(path[i], path[i+1]);
			r = Auxiliar.intIntersection(r, link.getWavelenghtsWithMaxAvailableBandWidth());
		}
		return r;
	}
	
	/**
	 * Retorna a quantidade de bandwidth disponivel em cada 
	 * wavelength do caminho domainPath.
	 * 
	 * @param domainPath
	 * @param resources
	 * @return
	 * @throws Exception
	 */
	public int[] getMultidomainAvailableWavelengths(int[] domainPath, int resources) throws Exception {
		int[] r = new int[wavelengths];
		for(int i=0; i<wavelengths; i++){
			r[i] = Integer.MAX_VALUE;
		}
		for(int i=0; i<domainPath.length-1; i++) {
			WDMLink link = getInterdomainLink(domainPath[i], domainPath[i+1]);
			for(int j=0; j<link.getWavelengths(); j++) {
				//if(link.getWavelengthAvailableBandwidth(j)<resources) {
					r[j] = Math.min(r[j], link.getWavelengthAvailableBandwidth(j));
				//}
			}
		}
		return r;
	}
	
	/**
	 * Retorna a quantidade de bandwidth disponivel em cada 
	 * wavelength do caminho domainPath, sendo que considerando as porcoes intradominio
	 * 
	 * @return
	 * @throws Exception
	 */
	public int[] getMultidomainAvailableWavelengths2(int[] domainPath, int borderNode) throws Exception {
		int[] r = new int[wavelengths];
		//Vector<OXC> OXCPath;
		for(int i=0; i<wavelengths; i++){
			r[i] = Integer.MAX_VALUE;
		}
		
		// adiciona o primeiro intradomain path
		for(int i=0; i<domainPath.length-1; i++) {
			//para cada inBorderNode
			for(int j=0; j<getNumBorderNodes(domainPath[i]); j++) {
				OXC inBorderNode = getBorderNode(domainPath[i], j);
				OXC outBorderNode = getOutBorderNode(domainPath[i], domainPath[i+1]);
				if(inBorderNode.equals(outBorderNode)) {
					//adiciona em OXCPath o caminho inBorderNode -> outBorderNode
					
				} else {
					//OXCPath.add(outBorderNode);
				}
				//WDMLink link = getInterdomainLink(domainPath[i], domainPath[i+1]);
				//adiciona em OXCPath link
			}
			
			
		}
		
		return r;
	}

	/**
	 * return a int[] domainPath given path
	 * @param path node's path
	 * @return
	 * @throws Exception 
	 */
	public int[] getDomainPathFromPath(int[] path) throws Exception {
		Vector<Integer> dp = new Vector<Integer>();
		dp.add(getDomainFromNode(path[0]).getID());
		
		for(int i=1; i<path.length; i++) {
			Domain domain = getDomainFromNode(path[i]);
			if(domain!=getDomainFromNode(path[i-1])) {
				dp.add(domain.getID());
			}
		}
		int[] r = new int[dp.size()];
		for(int i=0; i<dp.size(); i++) {
			r[i] = dp.get(i);
		}
		return r;
	}
	
	public int[] getDomainPathFromPath(Path path) throws Exception {
		Vector<WDMLink> links = path.getLightpath(0).getLinks();
		int[] p = new int[links.size()+1];
		int i = 0;
		for(i=0; i<links.size(); i++) {
			p[i] = links.get(i).getSrcId();
		}
		p[i] = links.lastElement().getDstId();
		
		return getDomainPathFromPath(p);
	}
	
	public int[] getPathFromPath(Path path) {
		Vector<WDMLink> links = path.getLightpath(0).getLinks();
		int[] p = new int[links.size()+1];
		int i = 0;
		for(i=0; i<links.size(); i++) {
			p[i] = links.get(i).getSrcId();
		}
		p[i] = links.lastElement().getDstId();
		
		return p;
	}
	
	public int[] getPathFromLightpath(Lightpath lp) {
		Vector<WDMLink> links = lp.getLinks();
		int[] p = new int[links.size()+1];
		int i = 0;
		for(i=0; i<links.size(); i++) {
			p[i] = links.get(i).getSrcId();
		}
		p[i] = links.lastElement().getDstId();
		
		return p;
	}

	/**
	 * return if path crosses the same domains (in the same order) as domainPath
	 * 
	 * @param domainPath
	 * @param path
	 * @return
	 * @throws Exception 
	 */
	public boolean isPathEquivalentMultidomainPath(int[] domainPath, int[] path) throws Exception {
		Vector<Integer> dp = new Vector<Integer>();
		int j = 1;
		dp.add(getDomainFromNode(path[0]).getID());
		
		for(int i=1; i<path.length; i++) {
			Domain domain = getDomainFromNode(path[i]);
			if(domain!=getDomainFromNode(path[i-1])) {
				if(domain.getID()==domainPath[j]) {
					dp.add(domain.getID());
					j++;
					
				} else {
					return false;
				}
			}
		}
		int[] a = new int[dp.size()];
		for(int i=0; i<dp.size(); i++) {
			a[i] = dp.get(i);
		}
		if(Auxiliar.arrayEqual(a, domainPath)) {
			return true;
			
		} else {
			return false;
		}
		
	}
	
	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
        String topo = "";
        for (int i = 0; i < nodes; i++) {
            for (int j = 0; j < nodes; j++) {
                if (adjacencyMatrix[i][j] != null) {
                    topo += adjacencyMatrix[i][j].toString() + "\n\n";
                }
            }
        }
        return topo;
    }

}
